using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using DynamicChart.Annotation;
using DynamicChart.Model;
using DynamicChart.Service.FunctionConversion;
using DynamicChart.Service.Interpreter;
using DynamicChart.Repository;
using DynamicChart.Service.Step;
using DynamicChart.Service.Step.Format;
using DynamicChart.Service.Step.GenerateSQL;
using DynamicChart.Util;

namespace DynamicChart.Service;

/// <summary>
/// 执行器
/// 通过执行器 执行 默认方法 sql
/// 或者 执行 执行方法加载数据
/// </summary>
public class ActuatorService
{
    private readonly LoadService _loadService;
    private readonly SQLRepository _sqlRepository;
    private readonly ISqlFunctionAdapter _sqlFunctionAdapter;
    private readonly DbTableStructureRepository _dbTableStructureRepository;
    private readonly ParamInterpreterEngine _paramInterpreterEngine;

    public ActuatorService(
        LoadService loadService, 
        SQLRepository sqlRepository, 
        ISqlFunctionAdapter sqlFunctionAdapter,
        DbTableStructureRepository dbTableStructureRepository,
        ParamInterpreterEngine paramInterpreterEngine
        )
    {
        _loadService = loadService;
        _sqlRepository = sqlRepository;
        _sqlFunctionAdapter = sqlFunctionAdapter;
        _dbTableStructureRepository = dbTableStructureRepository;
        _paramInterpreterEngine = paramInterpreterEngine;
    }
    
    /// <summary>
    /// 通过 传入参数 判断执行何种执行器
    /// 以及执行的条件参数
    /// </summary>
    /// <param name="param"></param>
    /// <returns></returns>
    public IDictionary<string, object> DoActuator(IDictionary<string, object> param)
    {
        IDictionary<string, object> res = new Dictionary<string, object>();
        
        if (param.TryGetValue("sql", out var sql) &&
            sql != null && 
            !string.IsNullOrEmpty(sql.ToString()))
        {
            res = DoActuatorSQL(param);
        }
        else
        {
            res = DoActuatorMethod(param);
        }
        
        return Formalize(param, res);
    }

    private IDictionary<string, object> DoActuatorMethod(IDictionary<string, object> param)
    {
        string signature = (param.TryGetValue("signature", out var signatureObj) ? signatureObj.ToString() : "")!;
        string[] parts = signature.Split('.');
        if (parts.Length < 2)
        {
            throw new ArgumentException("Invalid signature format. Expected 'Namespace.Class.Method'");
        }

        string classSignature = string.Join(".", parts.Take(parts.Length - 1));
        string methodSignature = parts.Last();

        Type targetType = Type.GetType(classSignature)!;
        if (targetType == null)
        {
            throw new InvalidOperationException($"Type '{classSignature}' not found.");
        }

        MethodInfo method = targetType.GetMethod(
            methodSignature,
            BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static
        )!;

        if (method == null)
        {
            throw new InvalidOperationException($"Method '{methodSignature}' not found in type '{classSignature}'.");
        }

        if (method.ReturnType != typeof(IDictionary<string, object>) &&
            method.ReturnType != typeof(IDictionary<string, Dictionary<string, object>>))
        {
            throw new InvalidOperationException(
                $"Method '{methodSignature}' returns an unsupported type: {method.ReturnType}");
        }

        object instance = (method.IsStatic ? null : Activator.CreateInstance(targetType))!;

        object result = method.Invoke(instance, new object[] { param })!;

        return result switch
        {
            IDictionary<string, object> dict => dict,
            IDictionary<string, Dictionary<string, object>> nestedDict => nestedDict.ToDictionary(kvp => kvp.Key, kvp => (object)kvp.Value as object),
            _ => throw new InvalidOperationException("Unexpected return type from method.")
        };
    }
    
    private IDictionary<string, object> DoActuatorSQL(IDictionary<string, object> param)
    {
        IDictionary<string, object> result = null;
        
        string sql = (param.TryGetValue("sql", out var sqlObj) ? sqlObj.ToString() : "")!;

        _paramInterpreterEngine.Run(ref sql, param);
            
        List<Dictionary<string, object>> data = _sqlRepository.ActuatorSQL(sql).Result;
            
        result = data
            .ToLookup(
                item => item["XAXIS"].ToString(),
                item => item
                    .Where(kvp => kvp.Key != "XAXIS")
                    .ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
            )
            .ToDictionary(g => g.Key, g => (object)g.Last());
        
        return result;
    }

    private IDictionary<string, object> Formalize(IDictionary<string, object> param, IDictionary<string, object> data)
    {
        var pipeline = new List<IFormatStep>
        {
            new TitleStep(),
            new AxisStep(),
            new LegendStep(_loadService),
            new SeriesStep(_loadService),
            new SeriesPieStep(_loadService),
            new YAxisStep()
        };

        var result = new Dictionary<string, object>
        {
            ["rawData"] = data
        };

        foreach (var step in pipeline)
        {
            step.Process(param, result);
        }

        // 移除中间数据
        result.Remove("rawData");
        return result;
    }

    public List<ActuatorModel> LoadActuatorModel(string name)
    {
        List<ActuatorModel> res = new List<ActuatorModel>();
        res = _loadService.LoadActuatorModel()!.ToList();
        if (!string.IsNullOrEmpty(name))
        {
            res = res.Where(t => t.Name!.Contains(name)).ToList();
        }
        
        return res;
    }

    public string CreateSQL(IDictionary<string, object> param)
    {
        var res = new Dictionary<string, string>();
        var pipeline = new List<IGenerateSQLStep>
        {
            new GenerateSelectStep(_sqlFunctionAdapter),
            new GenerateGroupStep(_sqlFunctionAdapter),
            new GenerateWhereStep(_sqlFunctionAdapter),
            new GenerateConnectionStep(_sqlFunctionAdapter),
            new GenerateSortStep(_sqlFunctionAdapter)
        };

        foreach (var step in pipeline)
        {
            step.Process(param, res);
        }

        var sql = new StringBuilder();
        sql.Append("SELECT ");

        if (res.TryGetValue("select", out var select))
        {
            sql.AppendLine(select);
        }
        else
        {
            sql.AppendLine("*");
        }

        sql.Append("FROM ");
        sql.AppendLine(string.Join(", ", param.Keys));

        if (res.TryGetValue("where", out var where))
        {
            sql.AppendLine($"WHERE {where}");
        }

        if (res.TryGetValue("group", out var group))
        {
            sql.AppendLine($"GROUP BY {group}");
        }

        if (res.TryGetValue("sort", out var sort))
        {
            sql.AppendLine($"ORDER BY {sort}");
        }

        return sql.ToString();
    }

    public IDictionary<string, object> LoadTableStructure(string name)
    {
        List<TableStructureModel> data = _dbTableStructureRepository.GetAll();

        if (!string.IsNullOrEmpty(name))
        {
            data = data.FindAll(i => i.TABLE_NAME.Contains(name));
        }

        IDictionary<string, object> dataTableTemp = new Dictionary<string, object>();

        foreach (var dataTable in data)
        {
            if (!dataTableTemp.ContainsKey(dataTable.TABLE_NAME))
            {
                dataTableTemp[dataTable.TABLE_NAME] = new Dictionary<string, object>();
                (dataTableTemp[dataTable.TABLE_NAME] as Dictionary<string, object>)!["comment"] =
                    dataTable.TABLE_COMMENT;
                (dataTableTemp[dataTable.TABLE_NAME] as Dictionary<string, object>)!["columnList"] =
                    new List<IDictionary<string, object>>();
            }

            IDictionary<string, object> dataTableField = new Dictionary<string, object>();
            dataTableField["columnName"] = dataTable.COLUMN_NAME;
            dataTableField["columnComment"] = dataTable.COLUMN_COMMENT;
            (((dataTableTemp[dataTable.TABLE_NAME] as Dictionary<string, object>)!["columnList"] as
                List<IDictionary<string, object>>)!).Add(dataTableField);
        }

        return dataTableTemp;
    }

    public Boolean SaveActuator(ActuatorModel actuatorModel)
    {
        if (actuatorModel.Id == null)
        {
            return _loadService.Add(actuatorModel);
        }
        else
        {
            return _loadService.Update(actuatorModel);
        }
    }

    public ActuatorModel GetById(int id)
    {
        return _loadService.LoadActuatorModel().First(i => i.Id == id);
    }
}